#include "ustandard/ustandard_sys.h"
#include "ustandard/umalloc.h"
struct umalloc_node_value {
    void*           ptr; 
};



#define NUM_NODES_IN_ARRAY  8

struct umalloc_node_value_array {
    unsigned int using;
    struct umalloc_node_value nodes[NUM_NODES_IN_ARRAY];
    struct umalloc_node_value_array* next;
};


int array_node_add(struct umalloc_node_value_array** list, 
        struct umalloc_node_value* add)
{
    int ret = 0;

    assert(NUM_NODES_IN_ARRAY<=sizeof(unsigned long)*8);
    struct umalloc_node_value_array* node_array_free = *list;
    struct umalloc_node_value* node_free = NULL;
    int i;
    while(NULL != node_array_free) {
        for(i=0; i<NUM_NODES_IN_ARRAY; i++) {
            if(!(node_array_free->using & (1<<i))) {
                node_free = &node_array_free->nodes[i];
                node_array_free->using |= (1<<i);
                break;
            }
        }

        if(NULL != node_free) {
            break;
        }

        node_array_free = node_array_free->next;
    }

    if(NULL == node_free) {
        node_array_free = malloc(sizeof(struct umalloc_node_value_array));
        //uloginf("######malloc addr %p\n", node_array_free);
        if(NULL == node_array_free) {
            //ulogdbg("malloc error%s.\n", "(task:alloc record node)");
            ret = -1;
        }
        else {
            node_free = &node_array_free->nodes[0];
            i = 0;
            node_array_free->using = (1<<i);

            node_array_free->next = *list;
            *list = node_array_free;
        }
        //ulogdbg("*list :  %p\n", *list);
    }

    if(NULL == node_free) {
        ret = -1;
    }
    else {
        *node_free = *add;
    }

    return ret;
}


int array_node_delete(struct umalloc_node_value_array** list, void* ptr)
{
    int ret = 0;

    assert(NUM_NODES_IN_ARRAY<=sizeof(unsigned long)*8);
    struct umalloc_node_value_array* node_array = *list;
    struct umalloc_node_value_array* node_array_prev = NULL;
    int i;
    int finish = 0;
    while(NULL != node_array) {
        /* traverse nodes array. */
        for(i=0; i<NUM_NODES_IN_ARRAY; i++) {
            if((node_array->using & (1<<i)) && ptr== node_array->nodes[i].ptr) {
                node_array->using &= ~(1<<i);

                /* if all the nodes in array not used, delete it. */
                if(0 == node_array->using) {
                    if(NULL == node_array_prev) {
                        *list = node_array->next;
                    }
                    else {
                        node_array_prev->next = node_array->next;
                    }
                    free(node_array);
                    node_array = NULL;
                }

                finish = 1;
                break;
            }
        }

        if(finish) {
            break;
        }

        node_array_prev = node_array;
        node_array= node_array->next;
    }

    ret = finish?0:-1;
    return ret;
}


struct umalloc_node {
    size_t          size; 
    const char*     file; 
    const char*     function; 
    unsigned int    line;

    unsigned long   num;
    struct umalloc_node_value_array*    list_values;

    struct umalloc_node* next;
};


/* linklist default header. */
static struct umalloc_node klist = {0};

/* set to enable monitor or not. */
static int kmonitor_on = 0;


static pthread_mutex_t klock = PTHREAD_MUTEX_INITIALIZER;
static int umalloc_lock(void)
{
    int ret = 0;
    ret = pthread_mutex_lock(&klock);
    return ret;
}


static int umalloc_unlock(void)
{
    int ret = 0;
    ret = pthread_mutex_unlock(&klock);
    return ret;
}


#define UMALLOC_ENABLE_LOG      0
#if UMALLOC_ENABLE_LOG
static FILE* kfp = NULL;
#define LEN_S   (10*1024)
#define __loglist()                                                               \
do {                                                                              \
    int retn = 0;                                                                 \
    char s[LEN_S] = {0};                                                          \
    size_t idx = 0;                                                               \
    size_t size = LEN_S;                                                          \
    struct umalloc_node* node = klist.next;                                       \
    while(NULL != node) {                                                         \
        int n = snprintf(s+idx, size-idx, "\t0x%08x %-10u at %-36s,%-20s,%-4d\n", \
        (unsigned int)(node->ptr),node->size,node->file,node->function,node->line);\
        if(n >= size-idx-1) {                                                     \
            retn = size-1;                                                        \
            break;                                                                \
        }                                                                         \
        else {                                                                    \
            idx += n;                                                             \
            retn += n;                                                            \
        }                                                                         \
        node = node->next;                                                        \
    }                                                                             \
    fprintf(kfp, "\t----------\n%s\t----------\n", s);                            \
    fprintf(stdout, "\t----------\n%s\t----------\n", s);                         \
}while(0);


#define __log(name, ptr, file, function, line)              \
do {                                                        \
    if(NULL == kfp) {                                       \
        kfp = fopen(".alloc.txt", "w");                     \
        setbuf(kfp, NULL);                                  \
        setbuf(stdout, NULL);                               \
    }                                                       \
    __loglist();                                            \
    fprintf(kfp, "----------%s : %8p %36s %36s %3u\n",      \
            name, ptr, file, function, line);               \
    fprintf(stdout, "----------%s : %8p %36s %36s %3u\n",   \
            name, ptr, file, function, line);               \
}while(0)


#else

#define __loglist()
#define __log(name, ptr, file, function, line)

#endif


struct umalloc_node*  
umalloc_record_find_and_add(size_t size, 
        const char* file, const char* function, unsigned int line)
{
    struct umalloc_node* retnode = NULL;
    struct umalloc_node* node = klist.next;
    while(NULL != node) {
        if(node->size == size
                && node->file == file
                && node->function == function
                && node->line == line) {
            break;
        }

        node = node->next;
    }

    retnode = node;
    return retnode;
}


static int umalloc_record_add(void* ptr, size_t size, 
        const char* file, const char* function, unsigned int line) {
    int ret = 0; 

    umalloc_lock();
    
    __log("add", ptr, file, function, line);
    struct umalloc_node* node_add = 
            umalloc_record_find_and_add(size, file, function, line);
    if(NULL == node_add) {
        node_add = malloc(sizeof(*node_add)); 
        if(NULL != node_add) {
            memset(node_add, 0, sizeof(*node_add));
            node_add->next      = klist.next;
            klist.next          = node_add;

            node_add->size          = size; 
            node_add->file          = file; 
            node_add->function      = function; 
            node_add->line          = line;
            node_add->num           = 0;
            node_add->list_values   = NULL;
        }
        else {
            ret = -1;
        }
    }

    if(NULL != node_add) {
        struct umalloc_node_value v;
        v.ptr = ptr;
        ret = array_node_add(&node_add->list_values, &v);
        if(0 != ret) {
            ulogerr("umalloc_record_add : array_node_add.\n");
            ret = -1;
        }
        else {
            node_add->num ++;
        }
    }

    __loglist();

    umalloc_unlock();

    return ret;
}


static int umalloc_record_del(void* ptr, 
        const char* file, const char* function, unsigned int line)
{
    int ret = 0; 

    umalloc_lock();

    __log("del", ptr, file, function, line);
    struct umalloc_node* node = klist.next;
    int finish = 0;
    while(NULL != node) {
        int ret_delete = array_node_delete(&node->list_values, ptr);
        if(0 == ret_delete) {
            finish = 1;
            break;
        }

        node = node->next;
    }

    if(!finish) {
        ulogerr("ufree ptr not found.\n");
        ret = -1;
    }
    else {
        node->num --;
    }
    __loglist();

    umalloc_unlock();

    return ret;
}


#if 0
static int umalloc_record_modify(void* ptr_new, size_t size_new, void* ptr, 
        const char* file, const char* function, unsigned int line)
{
    int ret = 0; 

    umalloc_lock();

    __log("del", ptr, file, function, line);
    __log("add", ptr_new, file, function, line);
    struct umalloc_node* node = klist.next;
    while(NULL != node) {
        if(node->ptr == ptr) {
            break;
        }
        node = node->next;
    }

    if(NULL != node) {
        node->ptr       = ptr_new; 
        node->size      = size_new; 
        node->file      = file; 
        node->function  = function; 
        node->line      = line;
    }
    else {
        ulogerr("urealloc ptr not found.\n");
        ret = -1;
    }
    __loglist();

    umalloc_unlock();

    return ret;
}
#endif


void* umalloc(size_t size, 
        const char* file, const char* function, unsigned int line)
{
    void* ptr = NULL;
    ptr = malloc(size);
    
    if(NULL != ptr) {
        if(kmonitor_on) {
            int ret = umalloc_record_add(ptr, size, file, function, line);
            if(0 != ret) {
                ulogerr("umalloc record add on %s: %s %d.\n", 
                        file, function, line);
            }
        }
    }
    else {
        ulogerr("umalloc error on %s: %s %d.\n", file, function, line);
    }

    return ptr;
}


int ufree(void* ptr, 
        const char* file, const char* function, unsigned int line)
{
    int ret = 0;

    if(NULL != ptr) {
        if(kmonitor_on) {
            ret = umalloc_record_del(ptr, file, function, line);
            if(0 != ret) {
                ulogerr("ufree %p error on %s: %s %d.\n", 
                        ptr,file, function, line);
            }
        }

        free(ptr);
        ptr = NULL;
    }
    else {
        ulogerr("ufree NULL pointer on %s: %s %d.\n", file, function, line);
        ret = -1;
    }

    return ret;
}


void* urealloc(void* ptr_ori, size_t size, 
        const char* file, const char* function, unsigned int line)
{
    void* ptr = NULL;
    ptr = realloc(ptr_ori, size);

    int ret = 0;
    if(NULL == ptr_ori) {
        if(NULL == ptr) {
            ulogerr("urealloc error on %s: %s %d.\n", file, function, line);
        }
        else {
            if(kmonitor_on) {
                ret = umalloc_record_add(ptr, size, file, function, line);
                if(0 != ret) {
                    ulogerr("umalloc record add on %s: %s %d.\n", 
                            file, function, line);
                }
            }
        }
    }
    else if(0 == size) {
        if(kmonitor_on) {
            ret = umalloc_record_del(ptr_ori, file, function, line);
            if(0 != ret) {
                ulogerr("ufree %p error on %s: %s %d.\n", 
                        ptr,file, function, line);
            }
        }
    }
    else {
        if(NULL == ptr) {
            ulogerr("urealloc error on %s: %s %d.\n", file, function, line);
        }
        else { 
            if(ptr != ptr_ori) {
                ulogdbg("urealloc addr changed.\n");
            }

            if(kmonitor_on) {
                ret = umalloc_record_add(ptr, size, file, function, line);
                if(0 != ret) {
                    ulogerr("umalloc record add on %s: %s %d.\n", 
                            file, function, line);
                }

                ret = umalloc_record_del(ptr_ori, file, function, line);
                if(0 != ret) {
                    ulogerr("ufree %p error on %s: %s %d.\n", 
                            ptr,file, function, line);
                }
            }
        }
    }

    return ptr;
}


int umalloc_query_o(char* s, size_t size)
{
    int retn = 0;

    umalloc_lock();
#if 0
    s[0] = '\0';
    size_t idx = 0;

    struct umalloc_node* node = klist.next;
    while(NULL != node) {
        int n = snprintf(s+idx, size-idx, "\t%p %-10u   at %-36s,%-20s,%-4d\n", 
                node->ptr, node->size, node->file, node->function, node->line);
        if(n >= size-idx-1) {
            retn = size-1;
            break;
        }
        else {
            idx += n;
            retn += n;
        }
        node = node->next;
    }
#endif
    umalloc_unlock();

    return retn;
}


#define UMALLOC_QUERY_EVENT_INFO                (1<<0)
#define UMALLOC_QUERY_EVENT_FILE                (1<<1)
#define UMALLOC_QUERY_EVENT_DETAIL              (1<<2)
int umalloc_query(char*s, size_t size)
{
    int retn = 0;
    int sidx = 0;
    int n = 0;
    int num_alloc = 0;
    struct umalloc_node* node = NULL;
    unsigned int event = 0;
    if(0 == event) {
        event = 
                UMALLOC_QUERY_EVENT_INFO | 
                UMALLOC_QUERY_EVENT_FILE |
                UMALLOC_QUERY_EVENT_DETAIL;
    }
    umalloc_lock();

    node = klist.next;
    while(NULL != node) {
        num_alloc += node->num;
        retn += node->num * node->size;

        node = node->next;
    }

    n = snprintf(s+sidx, size-sidx, 
        "umalloc result : num_alloc = %d, total_size = %u.\n", num_alloc, retn);
    if(n>=size-sidx-1) {
        goto finish;
    }
    sidx += n;

    node = klist.next;
    while(NULL != node) {
        if(node->num > 0) {
            n = snprintf(s+sidx, size-sidx, "\t%-36s, %-36s, %-6d, size:%-6d\n", 
                    node->file, node->function, node->line, node->size);
            if(n>=size-sidx-1) {
                goto finish;
            }
            sidx += n;

            struct umalloc_node_value_array* node_values = node->list_values;
            while(NULL != node_values) {
                int i;
                for(i=0; i<NUM_NODES_IN_ARRAY; i++) {
                    if(node_values->using & (1<<i)) {
                        n = snprintf(s+sidx, size-sidx, "\t\t%p\n", node_values->nodes[i].ptr);
                        if(n>=size-sidx-1) {
                            goto finish;
                        }
                        sidx += n;
                    }
                }
                node_values = node_values->next;
            }
        }

        node = node->next;
    }

finish:
    umalloc_unlock();
    return retn;
}


int umalloc_query_clear(void)
{
    int ret = 0; 

    umalloc_lock();
#if 0
    struct umalloc_node* node_free = NULL;
    while(NULL != klist.next) {
        node_free = klist.next;
        klist.next = klist.next->next;

        free(node_free);
        node_free = NULL;
    }
#endif
    umalloc_unlock();

    return ret;
}


int umalloc_enable(int set)
{
    int ret = 0;
    kmonitor_on = set;
    return ret;
}

